#ifndef _VC10EA_

#include <string.h> 

#include "BitLine.h"
#include "DeltaLine.h"
#include "Sort.h"
#include "MemManager.h"

//#define _TEST_
#ifdef  _TEST_
#include "Codec.h"
char Temp[1024*1024];
#endif

//			     0   1   2   3   4   5   6   7   8   9	
int Tab[101] = { 0,  0,  1,  2,  3,  4,  5,  6,  7,  8,		// 0
		   		 9, 10, 11, 11, 12, 12, 12, 13, 13, 13,		// 1
			    14, 14, 14, 14, 14, 13, 13, 13, 12, 12,		// 2
			    12, 11, 11, 10, 10,  9,  9,  8,  8,  8,		// 3
			     7,  7,  7,  6,  6,  5,  5,  4,  4,  3,		// 4
				 3,  3,  3,  3,  3,  3,  3,  3,  3,  3,		// 5
				 3,  3,  3,  3,  3,  3,  3,  3,  3,  3,		// 6	
				 3,  3,  3,  3,  3,  3,  3,  3,  3,  3,		// 7
				 3,  3,  3,  3,  3,  3,  3,  3,  3,  3,		// 8
				 3,  3,  3,  3,  3,  3,  3,  3,  3,  3,	 3};// 9

#else //ndef _VC10EA_

int Tab[101];

#endif //ndef _VC10EA_

short Quantum[512*(MaxQ+1)], Quantum_Rev[512*(MaxQ+1)], Quantum_Delta[512*(MaxQ+1)];


void Init_Quantum()
{
	int	V, Q, R, QQ, Vo, VR;

	for (Q=1; Q<=MaxQ; Q++) {
		for (V=-Q+1; V<=Q-1; V++) { Quantum[(Q<<9)+V+255]=0; Quantum_Delta[(Q<<9)+V+255]=0; }
		Quantum_Rev[(Q<<9)+255]=0;

		
		QQ = Q;
		R = 1;
		Vo = Q;

		while (Vo<=255) {
			V = Vo;
			//Vo += (QQ/3) + QQ;
			Vo += Tab[QQ] + QQ;
			VR = Min_(Vo-QQ, 255);
			for (; V<Min_(Vo,256); V++) {
				
				Quantum[(Q<<9) + V + 255] =  R;		Quantum_Delta[(Q<<9) + V + 255] =   VR;	
				Quantum[(Q<<9) - V + 255] = -R;		Quantum_Delta[(Q<<9) - V + 255] =  -VR;
			}
			Quantum_Rev[(Q<<9) + R + 255] = VR;
			Quantum_Rev[(Q<<9) - R + 255] = -VR;

			R++; 
			//QQ+=1;
		}
			


	}
}






INLINE int CPU_ID_1()
{
	_asm	{
		MOV		EAX,1
		CPUID
		MOV		EAX,EDX
		//MOV		EAX,0		 // NO MMX
		//MOV		EAX,1 shl 23 // NO SSE 
	}
}

/*================================================================================================================


						Sort_Pack_Deltas


================================================================================================================*/
INLINE void Sort_Pack_NOMMX(char *QDelta, short *Probability, char *SortDelta, int *NewIndex, int Delta_Pnt)
{
	
	__asm	{
		PUSHAD

		MOV		ESI,QDelta
		MOV		EDI,SortDelta
		MOV		EDX,Probability
		MOV		EBX,NewIndex



		MOV		ECX,Delta_Pnt
		SHR		ECX,2
		JE		Tail

		PUSH	EBP
Exec4:	
		MOV		EAX,[ESI]

		MOVZX	EBP,WORD PTR [EDX]
		INC		DWORD PTR [EBP*4+EBX]
		MOV		EBP,[EBP*4+EBX]
		MOV		[EDI+EBP-1],AL

		MOVZX	EBP,WORD PTR [EDX+2]
		INC		DWORD PTR [EBP*4+EBX]
		MOV		EBP,[EBP*4+EBX]
		MOV		[EDI+EBP-1],AH

		SHR		EAX,16

		MOVZX	EBP,WORD PTR [EDX+4]
		INC		DWORD PTR [EBP*4+EBX]
		MOV		EBP,[EBP*4+EBX]
		MOV		[EDI+EBP-1],AL

		MOVZX	EBP,WORD PTR [EDX+6]
		INC		DWORD PTR [EBP*4+EBX]
		MOV		EBP,[EBP*4+EBX]
		MOV		[EDI+EBP-1],AH

		ADD		EDX,8
		ADD		ESI,4

		LOOP	Exec4

		POP		EBP
		
Tail:	
		MOV		ECX,Delta_Pnt
		AND		ECX,3
		JE		Exit

		MOV		EAX,[ESI]
		PUSH	EBP
Exec1:	
		MOVZX	EBP,WORD PTR [EDX]
		INC		DWORD PTR [EBP*4+EBX]
		MOV		EBP,[EBP*4+EBX]
		MOV		[EDI+EBP-1],AL
		SHR		EAX,8
		ADD		EDX,2
		LOOP	Exec1
		POP		EBP

Exit:	POPAD

	}

}

#define	PrefetchLen	128
INLINE void Sort_Pack_MMX(char *QDelta, short *Probability, char *SortDelta, int *NewIndex, int Delta_Pnt)
{
	
	__asm	{
		PUSHAD

		MOV		ESI,QDelta
		MOV		EDI,SortDelta
		MOV		EDX,Probability
		MOV		EBX,NewIndex




		

ExecAny:
		CMP		Delta_Pnt,PrefetchLen
		JL		Less

		

		PREFETCHNTA	[EDX]
		PREFETCHNTA	[EDX+32]
		PREFETCHNTA	[EDX+32*2]
		PREFETCHNTA	[EDX+32*3]
		
		PREFETCHNTA	[EDX+32*4]
		PREFETCHNTA	[EDX+32*5]
		PREFETCHNTA	[EDX+32*6]
		PREFETCHNTA	[EDX+32*7]
		
		
		PREFETCHNTA	[ESI]
		PREFETCHNTA	[ESI+32]
		PREFETCHNTA	[ESI+32*2]
		PREFETCHNTA	[ESI+32*3]


		SUB		Delta_Pnt,PrefetchLen
		MOV		ECX,PrefetchLen/4

Exec:		


		PUSH	EBP
Exec4:	
		MOV		EAX,[ESI]

		MOVZX	EBP,WORD PTR [EDX]
		INC		DWORD PTR [EBP*4+EBX]
		MOV		EBP,[EBP*4+EBX]
		MOV		[EDI+EBP-1],AL

		MOVZX	EBP,WORD PTR [EDX+2]
		INC		DWORD PTR [EBP*4+EBX]
		MOV		EBP,[EBP*4+EBX]
		MOV		[EDI+EBP-1],AH

		SHR		EAX,16

		MOVZX	EBP,WORD PTR [EDX+4]
		INC		DWORD PTR [EBP*4+EBX]
		MOV		EBP,[EBP*4+EBX]
		MOV		[EDI+EBP-1],AL

		MOVZX	EBP,WORD PTR [EDX+6]
		INC		DWORD PTR [EBP*4+EBX]
		MOV		EBP,[EBP*4+EBX]
		MOV		[EDI+EBP-1],AH

		ADD		EDX,8
		ADD		ESI,4

		LOOP	Exec4

		POP		EBP

		JMP		ExecAny

Less:	MOV		ECX,Delta_Pnt
		AND		Delta_Pnt,3
		SHR		ECX,2
		JNE		Exec

		
Tail:	
		MOV		ECX,Delta_Pnt
		JECXZ	Exit

		MOV		EAX,[ESI]
		PUSH	EBP
Exec1:	
		MOVZX	EBP,WORD PTR [EDX]
		INC		DWORD PTR [EBP*4+EBX]
		MOV		EBP,[EBP*4+EBX]
		MOV		[EDI+EBP-1],AL
		SHR		EAX,8
		ADD		EDX,2
		LOOP	Exec1
		POP		EBP

Exit:	POPAD
		EMMS

	}

}

void Sort_Pack_Deltas(char * RESTRICT QDelta, short * RESTRICT Probability, int Delta_Pnt, int Q, BYTE* Pool, int *Start, 
					  int *NewIndex, BYTE * Memory)
{
	int i, A, F, MaxProb, P, Qshift;
	char *SortDelta;

	SortDelta  = (char*) ENCODER_MEMORY_GET_ADDRESS(ID_ENCODER_SortDelta,  Memory);

	//==================================================================== Build NewIndex for Sort 
	for (i=0, A=0; A<Delta_Pnt; i++) { 
		F = NewIndex[i];
		NewIndex[i] = A;
		A +=F;
	}
	MaxProb = i; 

	//=================================================================== Sort 
#ifdef _MMX_

	if (((CPU_ID_1()>>23) & 5)!=5) 
		Sort_Pack_NOMMX(QDelta, Probability, SortDelta, NewIndex, Delta_Pnt);
	else
		Sort_Pack_MMX(QDelta, Probability, SortDelta, NewIndex, Delta_Pnt);

#else
	for (i = 0; i<Delta_Pnt; i++) {
		P = Probability[i];
		SortDelta[NewIndex[P]] = QDelta[i];
		NewIndex[P]+=1;
	}
#endif
	memset(NewIndex,0,sizeof(int)*(MaxProb));		


	//================================================================== Pack
	Qshift = (Q<<9)+255;

#ifdef	_TEST_
	for (i=0; i<Delta_Pnt; i++) Temp[i]=SortDelta[i];
	A = *Start;
#endif

	Pack_Sign_QDelta_Stream(SortDelta, Delta_Pnt, Pool, Start, Quantum[Qshift+255], Memory);

#ifdef	_TEST_
	{
		BYTE *DecMemory;
		
		DecMemory = INIT_DECODER(320, 240);
		Unpack_Sign_QDelta_Stream(SortDelta, Delta_Pnt, Pool, &A, Quantum[Qshift+255], DecMemory);
		FREE_DECODER(DecMemory);
	}
	
	for (i=0; i<Delta_Pnt; i++) {
		if (Temp[i]!=SortDelta[i]) {
			_asm	HLT
		}
	}
#endif


}


/*================================================================================================================


						Unpack_ReSort_Deltas


================================================================================================================*/
void Unpack_Deltas(char *RESTRICT SortDelta, int Delta_Pnt, int Q, 
						  BYTE* RESTRICT Pool, int *RESTRICT Start, int *MaxProb, int *NewIndex, BYTE * Memory)
{	
	int	i, A, F, Qshift;

	//================================================================== Unpack
	Qshift = (Q<<9)+255;
	Unpack_Sign_QDelta_Stream(SortDelta, Delta_Pnt, Pool, Start, Quantum[Qshift+255], Memory);

	//==================================================================== Build NewIndex for reSort 
	for (i=0, A=0; A<Delta_Pnt; i++) { 
		F = NewIndex[i];
		NewIndex[i] = A;
		A +=F;
	}
	*MaxProb = i; 


}
